/*
 * ScrollBar.cpp
 *
 *  Created on: 03-12-2010
 *      Author: Piotr
 */

#include "gui/ScrollBar.hpp"
#include "gui/UIManager.hpp"
#include "gui/ClipArea.hpp"
#include "utils/GLUtils.hpp"

const float ScrollBar::SIZE = 15;

ScrollBar::ScrollBar(Alignment alignment) :
	range(RangedValue<int> (Range<int> (0, 100), 0)), alignment(alignment), singleHitChangeValue(10),
			increaseButton(new ArrowButton()), decreaseButton(new ArrowButton())
{
	add(increaseButton);
	add(decreaseButton);

	setOpaque(false);
	increaseButton->addMouseListener(this);
	decreaseButton->addMouseListener(this);
	increaseButton->addKeyListener(this);
	decreaseButton->addKeyListener(this);

	increaseButton->setSize(Dimension(SIZE, SIZE));
	decreaseButton->setSize(Dimension(SIZE, SIZE));
}

float ScrollBar::getScrolledPercent() const
{
	return range.getValue() / static_cast<float> (range.getRange().getMaximum());
}

ScrollBar::Alignment ScrollBar::getAlignment() const
{
	return alignment;
}

void ScrollBar::setBounds(const geometry::Rectangle& bounds)
{
	Control::setBounds(bounds);

	range.setRange(Range<int> (0, getScrollBarLength() - 3 * SIZE));
}

void ScrollBar::setSize(const Dimension& size)
{
	Control::setSize(size);

	range.setRange(Range<int> (0, getScrollBarLength() - 3 * SIZE));
}

void ScrollBar::draw(const ClipArea& clipArea)
{
	switch(alignment)
	{
		case VERTICAL:
			setVerticalComponentsPlacement();
			break;
		case HORIZONTAL:
			setHorizontalComponentsPlacement();
			break;
	}

	UIManager::getInstance().getScrollBarTrackColor().setGLState();
	GLUtils::drawRectangle(Vector(0, 0), getSize());

	Container::draw(clipArea);

	UIManager::getInstance().getButtonInsideColor().setGLState();
	clipArea.setGLClipArea();
	const float thumbPosition = getThumbPosition();
	switch(alignment)
	{
		case VERTICAL:
			GLUtils::drawRectangle(Vector(0.0f, thumbPosition), Dimension(SIZE, SIZE));
			break;
		case HORIZONTAL:
			GLUtils::drawRectangle(Vector(thumbPosition, 0.0f), Dimension(SIZE, SIZE));
			break;
	}
}

void ScrollBar::setVerticalComponentsPlacement()
{
	const Dimension& dimension = getSize();

	increaseButton->setPosition(Vector(0.0f, dimension.getHeight() - SIZE));
	decreaseButton->setPosition(Vector(0, 0));

	increaseButton->setAngle(180);
}

void ScrollBar::setHorizontalComponentsPlacement()
{
	const Dimension& dimension = getSize();

	increaseButton->setPosition(Vector(dimension.getWidth() - SIZE, 0.0f));
	decreaseButton->setPosition(Vector(0, 0));

	increaseButton->setAngle(90);
	decreaseButton->setAngle(270);
}

float ScrollBar::getScrollBarLength() const
{
	if(alignment == HORIZONTAL)
	{
		return getSize().getWidth();
	}
	return getSize().getHeight();
}

float ScrollBar::getThumbPosition() const
{
	int scrollBarLength = getScrollBarLength();
	float part = range.getValue() / static_cast<float> (range.getRange().getMaximum()
			- range.getRange().getMinimum());
	return SIZE + (scrollBarLength - 3 * SIZE) * part;
}

float ScrollBar::getMoveDifference(const Vector& nextPoint) const
{
	if(alignment == HORIZONTAL)
	{
		return nextPoint.getX() - prevPoint.getX();
	}
	return nextPoint.getY() - prevPoint.getY();
}

void ScrollBar::mousePressed(const MouseListener::EventArguments& arguments)
{
	if(arguments.getButton() == MouseListener::EventArguments::LEFT)
	{
		prevPoint = arguments.getPoint();
	}

	notifyMousePressed(arguments);
}

void ScrollBar::mouseDragged(const MouseListener::EventArguments& arguments)
{
	if(arguments.getButton() == MouseListener::EventArguments::LEFT && arguments.getSource() == this)
	{
		range.changeBy(getMoveDifference(arguments.getPoint()));
		prevPoint = arguments.getPoint();
	}

	notifyMouseDragged(arguments);
}

void ScrollBar::mouseReleased(const MouseListener::EventArguments& arguments)
{
	if(arguments.getSource() == increaseButton)
	{
		range.changeBy(singleHitChangeValue);
	}
	else if(arguments.getSource() == decreaseButton)
	{
		range.changeBy(-singleHitChangeValue);
	}

	notifyMouseReleased(arguments);
}

void ScrollBar::keyPressed(const KeyListener::EventArguments& arguments)
{
	switch(alignment)
	{
		case HORIZONTAL:
			moveScrollbarHorizontallyWithKey(arguments.getKey());
			break;
		case VERTICAL:
			moveScrollbarVerticallyWithKey(arguments.getKey());
			break;
	}
}

void ScrollBar::moveScrollbarHorizontallyWithKey(SDLKey key)
{
	if(key == SDLK_LEFT)
	{
		range.changeBy(-singleHitChangeValue);
	}
	else if(key == SDLK_RIGHT)
	{
		range.changeBy(singleHitChangeValue);
	}
}

void ScrollBar::moveScrollbarVerticallyWithKey(SDLKey key)
{
	if(key == SDLK_UP)
	{
		range.changeBy(-singleHitChangeValue);
	}
	else if(key == SDLK_DOWN)
	{
		range.changeBy(singleHitChangeValue);
	}
}
